package com.ssy.lingxi.order.serviceimpl.base;

import com.ssy.lingxi.common.constant.contract.ContractSourceTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.entity.OrderPurchaseProcessContractDO;
import com.ssy.lingxi.order.entity.OrderPurchaseProcessDO;
import com.ssy.lingxi.order.model.constant.OrderServiceContants;
import com.ssy.lingxi.order.model.vo.process.request.OrderPurchaseProcessContractVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderPurchaseProcessContractQueryVO;
import com.ssy.lingxi.order.repository.OrderPurchaseProcessContractRepository;
import com.ssy.lingxi.order.service.base.IBaseOrderPurchaseProcessContractService;
import com.ssy.lingxi.order.utils.NumberUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 采购流程规则关联的合同设置相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-07-25
 */
@Service
public class BaseOrderPurchaseProcessContractServiceImpl implements IBaseOrderPurchaseProcessContractService {
    @Resource
    private OrderPurchaseProcessContractRepository orderPurchaseProcessContractRepository;

    /**
     * 新增、更新采购流程时，校验关联的合同
     *
     * @param purchaseProcesses 已存在的同类型的采购流程规则
     * @param allContracts      是否适用所有合同
     * @param contractList      合同列表
     * @return 操作结果
     */
    @Override
    public Wrapper<Void> checkContracts(List<OrderPurchaseProcessDO> purchaseProcesses, Boolean allContracts, List<OrderPurchaseProcessContractVO> contractList) {
        //如果适用于所有合同，判断是否有另一个相同的采购流程也适用于所有合同，或已配置了关联的合同
        if(allContracts) {
            if(CollectionUtils.isEmpty(purchaseProcesses)) {
                return Wrapper.success();
            }

            if(purchaseProcesses.stream().anyMatch(OrderPurchaseProcessDO::getAllContracts)) {
                return Wrapper.fail(ResponseCode.ORDER_PURCHASE_PROCESS_ALL_PRODUCTS_EXIST);
            }

            if(purchaseProcesses.stream().anyMatch(process -> !CollectionUtils.isEmpty(process.getContracts()))) {
                return Wrapper.fail(ResponseCode.ORDER_PURCHASE_PROCESS_EXIST_WITH_CONTRACTS);
            }
        } else {
            //合同不能为空
            if(CollectionUtils.isEmpty(contractList)) {
                return Wrapper.fail(ResponseCode.ORDER_PURCHASE_PROCESS_CONTRACT_CAN_NOT_BE_EMPTY);
            }

            //校验合同Id是否重复
            if(contractList.size() != contractList.stream().map(OrderPurchaseProcessContractVO::getContractId).distinct().count()) {
                return Wrapper.fail(ResponseCode.ORDER_PURCHASE_PROCESS_PRODUCT_ID_DUPLICATE);
            }

            if(CollectionUtils.isEmpty(purchaseProcesses)) {
                return Wrapper.success();
            }

            if(purchaseProcesses.stream().anyMatch(OrderPurchaseProcessDO::getAllContracts)) {
                return Wrapper.fail(ResponseCode.ORDER_PURCHASE_PROCESS_ALL_PRODUCTS_EXIST);
            }

            //判断合同是否存在于另一个流程中
            Specification<OrderPurchaseProcessContractDO> specification = (Specification<OrderPurchaseProcessContractDO>) (root, query, criteriaBuilder) -> {
                List<Predicate> list = new ArrayList<>();
                list.add(root.get("process").as(OrderPurchaseProcessDO.class).in(purchaseProcesses));
                list.add(root.get("contractId").as(Long.class).in(contractList.stream().map(OrderPurchaseProcessContractVO::getContractId).collect(Collectors.toList())));
                Predicate[] p = new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(p));
            };

            if(orderPurchaseProcessContractRepository.count(specification) > 0) {
                return Wrapper.fail(ResponseCode.ORDER_PURCHASE_PROCESS_CONTRACT_EXIST_ANOTHER_PROCESS);
            }
        }

        return Wrapper.success();
    }

    /**
     * 检查、新增采购流程关联的合同，调用方要保存 OrderPurchaseProcessDO
     *
     * @param purchaseProcess 采购流程规则
     * @param allContracts    是否适用所有合同
     * @param contractList    合同列表
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void saveContracts(OrderPurchaseProcessDO purchaseProcess, Boolean allContracts, List<OrderPurchaseProcessContractVO> contractList) {
        if(allContracts) {
            purchaseProcess.setContracts(new HashSet<>());
            return;
        }

        List<OrderPurchaseProcessContractDO> contracts = contractList.stream().map(contractVO -> {
            OrderPurchaseProcessContractDO contract = new OrderPurchaseProcessContractDO();
            contract.setProcess(purchaseProcess);
            contract.setContractId(contractVO.getContractId());
            contract.setContractNo(contractVO.getContractNo());
            contract.setDigest(contractVO.getDigest());
            contract.setEffectTime(LocalDateTime.parse(contractVO.getEffectTime().concat(" 00:00:00"), OrderServiceContants.DEFAULT_TIME_FORMATTER));
            contract.setExpireTime(LocalDateTime.parse(contractVO.getExpireTime().concat(" 23:59:59"), OrderServiceContants.DEFAULT_TIME_FORMATTER));
            contract.setPartyB(contractVO.getPartyB());
            contract.setAmount(contractVO.getAmount());
            contract.setContractType(contractVO.getContractType());
            contract.setReceiptNo(StringUtils.hasLength(contractVO.getReceiptNo()) ? contractVO.getReceiptNo() : "");
            return contract;
        }).collect(Collectors.toList());

        orderPurchaseProcessContractRepository.saveAll(contracts);

        //设置关联
        purchaseProcess.setContracts(new HashSet<>(contracts));
    }

    /**
     * 分页查询采购流程关联的合同列表
     *
     * @param purchaseProcess 采购流程规则
     * @param digest          合同摘要
     * @param current         当前页
     * @param pageSize        每页行数
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<OrderPurchaseProcessContractQueryVO>> pageContracts(OrderPurchaseProcessDO purchaseProcess, String digest, int current, int pageSize) {
        Pageable pageable = PageRequest.of(current - 1, pageSize, Sort.by("id").ascending());
        Specification<OrderPurchaseProcessContractDO> specification = (Specification<OrderPurchaseProcessContractDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("process").as(OrderPurchaseProcessDO.class), purchaseProcess));

            if(StringUtils.hasLength(digest)) {
                list.add(criteriaBuilder.like(root.get("digest").as(String.class), "%" + digest.trim() + "%"));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        Page<OrderPurchaseProcessContractDO> pageList = orderPurchaseProcessContractRepository.findAll(specification, pageable);
        return Wrapper.success(new PageData<>(pageList.getTotalElements(), pageList.getContent().stream().map(contract ->{
            OrderPurchaseProcessContractQueryVO queryVO = new OrderPurchaseProcessContractQueryVO();
            queryVO.setContractId(contract.getContractId());
            queryVO.setContractNo(contract.getContractNo());
            queryVO.setDigest(contract.getDigest());
            queryVO.setEffectTime(contract.getEffectTime().format(OrderServiceContants.DEFAULT_DATE_FORMATTER));
            queryVO.setExpireTime(contract.getExpireTime().format(OrderServiceContants.DEFAULT_DATE_FORMATTER));
            queryVO.setPartyB(contract.getPartyB());
            queryVO.setAmount(NumberUtil.formatAmount(contract.getAmount()));
            queryVO.setContractType(contract.getContractType());
            queryVO.setContractTypeName(ContractSourceTypeEnum.getNameByCode(contract.getContractType()));
            queryVO.setReceiptNo(contract.getReceiptNo());
            return queryVO;
        }).collect(Collectors.toList())));
    }

    /**
     * 更新采购流程关联的合同，调用方要保存 OrderPurchaseProcessDO
     *
     * @param purchaseProcess 采购流程规则
     * @param allContracts    是否适用所有合同
     * @param contractList    合同列表
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void updateContracts(OrderPurchaseProcessDO purchaseProcess, Boolean allContracts, List<OrderPurchaseProcessContractVO> contractList) {
        if(allContracts) {
            orderPurchaseProcessContractRepository.deleteByProcess(purchaseProcess);
            purchaseProcess.setContracts(new HashSet<>());
            return;
        }

        //先删除再保存
        orderPurchaseProcessContractRepository.deleteByProcess(purchaseProcess);

        List<OrderPurchaseProcessContractDO> contracts = contractList.stream().map(contractVO -> {
            OrderPurchaseProcessContractDO contract = new OrderPurchaseProcessContractDO();
            contract.setProcess(purchaseProcess);
            contract.setContractId(contractVO.getContractId());
            contract.setContractNo(contractVO.getContractNo());
            contract.setDigest(contractVO.getDigest());
            contract.setEffectTime(LocalDateTime.parse(contractVO.getEffectTime().concat(" 00:00:00"), OrderServiceContants.DEFAULT_TIME_FORMATTER));
            contract.setExpireTime(LocalDateTime.parse(contractVO.getExpireTime().concat(" 23:59:59"), OrderServiceContants.DEFAULT_TIME_FORMATTER));
            contract.setPartyB(contractVO.getPartyB());
            contract.setAmount(contractVO.getAmount());
            contract.setContractType(contractVO.getContractType());
            contract.setReceiptNo(StringUtils.hasLength(contractVO.getReceiptNo()) ? contractVO.getReceiptNo() : "");
            return contract;
        }).collect(Collectors.toList());

        orderPurchaseProcessContractRepository.saveAll(contracts);

        //设置关联
        purchaseProcess.setContracts(new HashSet<>(contracts));
    }
}
